/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package aimozg.joglfw.gui;

import aimozg.joglfw.GLColor;
import aimozg.joglfw.GLPoint;
import aimozg.joglfw.JoglApplication;
import aimozg.utils.ListenerList;
import aimozg.utils.ListenerPublicator;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.List;
import javax.media.opengl.GL;

/**
 *
 * @author aimozg
 */
public abstract class GLComponent {
    protected JoglApplication app;
    protected GLComponent owner;
    protected List<GLComponent> components = new LinkedList<GLComponent>();
    protected double x;
    protected double y;
    protected double width;
    protected double height;
    protected boolean visible = true;

    public boolean isVisible() {
        return visible;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }
    
    private GLComponent focusedChild = null;
    public boolean isFocused(){
        return (owner == null || this == owner.focusedChild);
    }
    public void gainFocus(){
        if (owner != null){
            owner.focusedChild = this;
        }
        focusedChild = null;
    }

    protected ListenerList<GLComponent,MouseEvent> llOnClick = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onClick = llOnClick.publicator();
    protected ListenerList<GLComponent,MouseEvent> llOnMouseDown = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onMouseDown = llOnMouseDown.publicator();
    protected ListenerList<GLComponent,MouseEvent> llOnMouseUp = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onMouseUp = llOnMouseUp.publicator();
    protected ListenerList<GLComponent,MouseEvent> llOnMouseEnter = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onMouseEnter = llOnMouseEnter.publicator();
    protected ListenerList<GLComponent,MouseEvent> llOnMouseExit = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onMouseExit = llOnMouseExit.publicator();
    protected ListenerList<GLComponent,MouseEvent> llOnMouseMove = new ListenerList<GLComponent, MouseEvent>();
    public final ListenerPublicator<GLComponent,MouseEvent> onMouseMove = llOnMouseMove.publicator();
    protected ListenerList<GLComponent,KeyEvent> llOnKeyDown = new ListenerList<GLComponent, KeyEvent>();
    public final ListenerPublicator<GLComponent,KeyEvent> onKeyDown = llOnKeyDown.publicator();
    protected ListenerList<GLComponent,KeyEvent> llOnKeyType = new ListenerList<GLComponent, KeyEvent>();
    public final ListenerPublicator<GLComponent,KeyEvent> onKeyType = llOnKeyType.publicator();
    protected ListenerList<GLComponent,KeyEvent> llOnKeyUp = new ListenerList<GLComponent, KeyEvent>();
    public final ListenerPublicator<GLComponent,KeyEvent> onKeyUp = llOnKeyUp.publicator();
    
    public void mouseEvent(MouseEvent e){
        MouseEvent te = new MouseEvent(
                e.getComponent(),
                e.getID(),
                e.getWhen(),e.getModifiers(),
                (int)(e.getX()-x),
                (int)(e.getY()-y),
                e.getX(),
                e.getY(),
                e.getClickCount(),
                e.isPopupTrigger(),
                e.getButton());
        if (e.getID() == MouseEvent.MOUSE_CLICKED){
            gainFocus();
            llOnClick.fire(this, te);
        }
        if (e.getID() == MouseEvent.MOUSE_PRESSED) llOnMouseDown.fire(this, te);
        if (e.getID() == MouseEvent.MOUSE_RELEASED) llOnMouseUp.fire(this, te);
        if (e.getID() == MouseEvent.MOUSE_ENTERED) llOnMouseEnter.fire(this, te);
        if (e.getID() == MouseEvent.MOUSE_EXITED) llOnMouseExit.fire(this, te);
        for (GLComponent c : components) {
            if (te.isConsumed()) break;
            if (c.getX()<=te.getX() &&
                    c.getY() <= te.getY() &&
                    c.getX()+c.getWidth() >= te.getX() &&
                    c.getY()+c.getHeight() >= te.getY()){
                c.mouseEvent(te);
            }
        }
    }
    public void keyboardEvent(KeyEvent e){
        if (e.getID() == KeyEvent.KEY_PRESSED) llOnKeyDown.fire(this,e);
        if (e.getID() == KeyEvent.KEY_RELEASED) llOnKeyUp.fire(this,e);
        if (e.getID() == KeyEvent.KEY_TYPED) llOnKeyType.fire(this,e);
        for (GLComponent c : components) {
            if (e.isConsumed()) break;
            c.keyboardEvent(e);
        }
    }

    public final GLPoint topleft(){
        return new GLPoint(0, 0);
    }
    public final GLPoint topright(){
        return new GLPoint(width, 0);
    }
    public final GLPoint bottomright(){
        return new GLPoint(width, height);
    }
    public final GLPoint bottomleft(){
        return new GLPoint(0, height);
    }
    public final double getBottom() {
        return y + height;
    }
    public final double getRight() {
        return x + width;
    }
    
    public final double getHeight() {
        return height;
    }

    public final double getWidth() {
        return width;
    }

    public final double getX() {
        return x;
    }

    public final double getY() {
        return y;
    }

    public final GLComponent getOwner() {
        return owner;
    }

    public final void setWidth(double width) {
        this.width = width;
    }

    public final void setHeight(double height) {
        this.height = height;
    }

    public final void setX(double x) {
        this.x = x;
    }

    public final void setY(double y) {
        this.y = y;
    }

    public final void setPos(double x, double y){
        this.x = x;
        this.y = y;
    }

    public final void setSize(double width, double height){
        this.width = width;
        this.height = height;
    }

    public final void setDims(double x, double y, double width, double height){
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public final void setRect(double left, double right, double top, double bottom){
        this.x = left;
        this.y = top;
        this.width = right-left;
        this.height = bottom-top;
    }

    public final void setOwner(GLComponent owner) {
        this.owner = owner;
        owner.components.add(this);
    }

    protected boolean overflowAllowed(){return false;}
    
    public void render(){
        if (!visible) return;
        app.translate(x, y, 0);
        if (!overflowAllowed()){
            app.gl().glClear(GL.GL_STENCIL_BUFFER_BIT);
            app.gl().glStencilFunc(GL.GL_NEVER,0,0);
            app.gl().glStencilOp(GL.GL_INCR,GL.GL_INCR,GL.GL_INCR);
            app.drawQuads(
		            new GLColor(1, 1, 1),
		            new GLPoint(-1, -1),
		            new GLPoint(width + 1, -1),
		            new GLPoint(width + 1, height + 1),
		            new GLPoint(-1, height + 1));
            app.gl().glStencilFunc(GL.GL_EQUAL,1,1);
            app.gl().glStencilOp(GL.GL_KEEP,GL.GL_KEEP,GL.GL_KEEP);
        }
        draw();
        for (GLComponent gLComponent : components) {
            gLComponent.render();
        }
        app.translate(-x,-y,0);
    }

    protected abstract void draw();

    public GLComponent(JoglApplication app) {
        this.app = app;
    }

    public GLComponent(GLComponent owner) {
        if (owner == null) throw new IllegalArgumentException("Null owner of GLComponent");
        this.app = owner.app;
        setOwner(owner);
    }



}
